#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/log.h>

int media_mux(const char *in_filename, const char *out_filename)
{
    AVFormatContext *ifmt_ctx = NULL;
    AVFormatContext *ofmt_ctx = NULL;
    AVPacket pkt;
    int ret = 0;
    int stream_index = 0;
    int *stream_mapping = NULL;
    int stream_mapping_size = 0;

    // 打开输入文件
    if ((ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL)) < 0) {
        av_log(NULL, AV_LOG_ERROR, "无法打开输入文件 '%s'\n", in_filename);
        goto end;
    }

    // 获取输入文件信息
    if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) {
        av_log(NULL, AV_LOG_ERROR, "获取输入流信息失败\n");
        goto end;
    }

    // 创建输出上下文
    avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
    if (!ofmt_ctx) {
        av_log(NULL, AV_LOG_ERROR, "无法创建输出上下文\n");
        ret = AVERROR_UNKNOWN;
        goto end;
    }

    stream_mapping_size = ifmt_ctx->nb_streams;
    stream_mapping = av_mallocz_array(stream_mapping_size, sizeof(*stream_mapping));
    if (!stream_mapping) {
        ret = AVERROR(ENOMEM);
        goto end;
    }

    // 为输出文件创建流
    for (unsigned int i = 0; i < ifmt_ctx->nb_streams; i++) {
        AVStream *out_stream;
        AVStream *in_stream = ifmt_ctx->streams[i];
        AVCodecParameters *in_codecpar = in_stream->codecpar;

        if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO &&
            in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO &&
            in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
            stream_mapping[i] = -1;
            continue;
        }

        stream_mapping[i] = stream_index++;
        out_stream = avformat_new_stream(ofmt_ctx, NULL);
        if (!out_stream) {
            av_log(NULL, AV_LOG_ERROR, "创建输出流失败\n");
            ret = AVERROR_UNKNOWN;
            goto end;
        }

        // 复制编解码器参数
        ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);
        if (ret < 0) {
            av_log(NULL, AV_LOG_ERROR, "复制编解码器参数失败\n");
            goto end;
        }
    }

    // 打开输出文件
    if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {
        ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
        if (ret < 0) {
            av_log(NULL, AV_LOG_ERROR, "无法打开输出文件 '%s'\n", out_filename);
            goto end;
        }
    }

    // 在写入文件头之前添加日志
    av_log(NULL, AV_LOG_INFO, "输入文件信息:\n");
    av_dump_format(ifmt_ctx, 0, in_filename, 0);
    
    // 写入文件头
    ret = avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "写入文件头失败\n");
        goto end;
    }

    // 添加输出文件信息日志
    av_log(NULL, AV_LOG_INFO, "\n输出文件信息:\n");
    av_dump_format(ofmt_ctx, 0, out_filename, 1);

    // 读取数据包并写入
    while (1) {
        ret = av_read_frame(ifmt_ctx, &pkt);
        if (ret < 0)
            break;

        int stream_idx = pkt.stream_index;
        if (stream_idx >= stream_mapping_size ||
            stream_mapping[stream_idx] < 0) {
            av_packet_unref(&pkt);
            continue;
        }

        pkt.stream_index = stream_mapping[stream_idx];
        av_packet_rescale_ts(&pkt,
                            ifmt_ctx->streams[stream_idx]->time_base,
                            ofmt_ctx->streams[pkt.stream_index]->time_base);

        ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
        if (ret < 0) {
            av_log(NULL, AV_LOG_ERROR, "写入数据包失败\n");
            break;
        }
        av_packet_unref(&pkt);
    }

    // 在写入文件尾之前添加统计信息
    av_log(NULL, AV_LOG_INFO, "转换完成，共处理 %d 个数据包\n", stream_index);

    // 写入文件尾
    av_write_trailer(ofmt_ctx);

    // 在end标签前添加文件大小检查
    int64_t input_size = avio_size(ifmt_ctx->pb);
    int64_t output_size = avio_size(ofmt_ctx->pb);
    
    if (output_size <= 0 || (input_size > 0 && output_size < input_size * 0.5)) {
        av_log(NULL, AV_LOG_WARNING, "警告：输出文件大小异常 (输入: %"PRId64" 字节, 输出: %"PRId64" 字节)\n", 
               input_size, output_size);
    } else {
        av_log(NULL, AV_LOG_INFO, "转换成功！输出文件大小: %"PRId64" 字节\n", output_size);
    }

end:
    if (stream_mapping)
        av_freep(&stream_mapping);
    if (ofmt_ctx && !(ofmt_ctx->oformat->flags & AVFMT_NOFILE))
        avio_closep(&ofmt_ctx->pb);
    if (ofmt_ctx)
        avformat_free_context(ofmt_ctx);
    if (ifmt_ctx)
        avformat_close_input(&ifmt_ctx);

    return ret < 0 ? 1 : 0;
}

// 主函数示例
int main(int argc, char **argv)
{
    if (argc != 3) {
        printf("用法: %s <输入文件> <输出文件>\n", argv[0]);
        return 1;
    }

    return media_mux(argv[1], argv[2]);
} 
/*
这段代码实现了以下功能：
1.打开输入媒体文件并读取其流信息
2.创建输出媒体文件的上下文
3. 将输入流复制到输出文件中
4.处理音频、视频和字幕流
5.进行时间戳的重新计算
6.正确处理内存的分配和释放


sudo apt-get install libavformat-dev libavcodec-dev libavutil-dev
gcc -o media_mux media_mux.c -lavformat -lavcodec -lavutil
./media_mux input.mp4 output.mkv


# 使用ffmpeg查看文件信息
ffmpeg -i output.mkv

# 使用mediainfo查看详细信息
mediainfo output.mkv

# 使用ffprobe查看流信息
ffprobe output.mkv

完整性检查：
ffmpeg -v error -i output.mkv -f null - 2>error.log

*/